﻿using NiceControls;
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace SimpleTerminal.Controls
{
    [ToolboxItem(false)]
    public partial class BaseComboBox : Control
    {
        protected int selectedIndex = -1;
        protected delegate void ComboBoxHandler<T>(T eventArgs);
        protected int maximumHeight = 300;
        protected StringFormat stringFormat;
        protected ComboBoxList comboBoxList;
        protected NPopup dropDown;
        protected Color backgroundColor = Color.White;
        private ObjectCollection<object> items;

        [Description("当选项改变时触发此事件")]
        [Category("自定义事件")]
        public event EventHandler SelectedIndexChanaged;

        [Browsable(false)]
        public bool DropDownListOpened { get; set; }

        private bool CanShowDropdown = true;

        [Category("自定义属性")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Localizable(false)]
        [MergableProperty(false)]
        [Browsable(false)]
        public virtual ObjectCollection<object> Items
        {
            get { return items; }
            protected set { items = value; }
        }


        [Browsable(false)]
        [Localizable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new string Text { get; set; }

        [Browsable(false)]
        public new bool Focused
        {
            get;
            protected set;
        }

        [Category("自定义属性")]
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int SelectedIndex
        {
            get
            {
                return selectedIndex;
            }
            set
            {
                if (value < 0 || value >= Items.Count)
                    return;
                if (selectedIndex != value)
                {
                    selectedIndex = value;
                    OnSelectedIndexChanged(EventArgs.Empty);
                    Invalidate();
                }
            }
        }

        protected new virtual void OnTextChanged(EventArgs e)
        {
        }



        [Category("自定义属性")]
        [Description("背景颜色")]
        [Browsable(true)]
        public Color BackgroundColor
        {
            get { return backgroundColor; }
            set { backgroundColor = value; }
        }


        public BaseComboBox()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            SetStyle(ControlStyles.StandardDoubleClick, false);

            Items = new ObjectCollection<object>();

            InitializeComponent();
            stringFormat = new StringFormat
            {
                Alignment = StringAlignment.Near,
                LineAlignment = StringAlignment.Center,
                FormatFlags = StringFormatFlags.LineLimit
            };

            comboBoxList = new ComboBoxList(this)
            {
                HoverIndex = selectedIndex,
                Padding = Padding,
                BackColor = Color.White
            };
            this.ImeMode = ImeMode.Disable;

            dropDown = new NPopup(comboBoxList);
            dropDown.DropShadowEnabled = false;
            dropDown.Closed += DropDownList_FormClosed;
            // contaniner.Closed += DropDownList_FormClosed;
            UpdateData();
            // CLButtonDownFilter MyFilter = new CLButtonDownFilter();
            //Application.AddMessageFilter(MyFilter);
        }

        [Category("自定义属性"), Description("下拉框最大高度"), Browsable(true)]
        protected int MaximumHeight { get => maximumHeight; set => maximumHeight = value; }

        bool mouseHover = false;

        protected override void WndProc(ref Message m)
        {


            base.WndProc(ref m);
        }

        protected virtual void UpdateData()
        { }

        protected override void OnClick(EventArgs e)
        {
            base.OnClick(e);

        }
        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            mouseHover = true;
            Invalidate();
            // Console.WriteLine("鼠标进入");
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            mouseHover = false;
            Invalidate();
            //Console.WriteLine("鼠标离开");
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            // Console.WriteLine("鼠标移动");
        }

        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            // Console.WriteLine("获得焦点");
            Focused = true;
            Invalidate();
        }
        protected override void OnLostFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            //Console.WriteLine("丢失焦点");
            Focused = false;
            Invalidate();
        }


        protected virtual void OnSelectedIndexChanged(EventArgs eventArgs)
        {
            SelectedIndexChanaged?.Invoke(this, eventArgs);
        }

        protected virtual void DropDownList_FormClosed(object sender, EventArgs e)
        {
            Point point = Parent.PointToClient(new Point(MousePosition.X, MousePosition.Y));
            if (Bounds.Contains(point))
                CanShowDropdown = false;
            else
                CanShowDropdown = true;
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            if (e.Delta > 0)
            {
                if (selectedIndex > 0)
                    SelectedIndex--;
            }
            else
            {
                if (selectedIndex < Items.Count - 1)
                    SelectedIndex++;
            }

        }


        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            if (e.KeyCode == Keys.Enter)
            {

                if (!Focused)
                    Focus();
                ShowDropDown();
            }
        }
        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
          //  Console.WriteLine("点击");
            if (!Focused)
                Focus();
            if (e.Button == MouseButtons.Left)
            {
                ShowDropDown();
            }
        }

        protected virtual void ShowDropDown()
        {
            if (!CanShowDropdown)
            {
                if (dropDown.Visible)
                    dropDown.Close();
                CanShowDropdown = true;
                return;
            }
            CanShowDropdown = false;
            OnComboBoxListOpening();
            comboBoxList.NotifyUpdateData();
            comboBoxList.HoverIndex = selectedIndex;
            dropDown.Show(this);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (e.Button == MouseButtons.Left)
            {
                Invalidate();
            }
        }
        public GraphicsPath GetArrowRect(Rectangle rectangle, int v)
        {
            GraphicsPath path = new GraphicsPath();
            path.StartFigure();
            path.AddLine(rectangle.X, rectangle.Y, rectangle.Right, rectangle.Y);
            path.AddLine(rectangle.Right, rectangle.Y, rectangle.X + rectangle.Width / 2, rectangle.Bottom);
            path.AddLine(rectangle.X + rectangle.Width / 2, rectangle.Bottom, rectangle.X, rectangle.Y);
            path.CloseFigure();
            return path;
        }

        public int Radius = 0;

        public GraphicsPath GetRoundRect(Rectangle rectangle, int radius)
        {
            GraphicsPath path = new GraphicsPath();
            int diameter = radius * 2;
            path.StartFigure();
            if (radius == 0)
            {
                path.AddLine(rectangle.X, rectangle.Y, rectangle.Right, rectangle.Y);
                path.AddLine(rectangle.Right, rectangle.Y, rectangle.Right, rectangle.Bottom);
                path.AddLine(rectangle.Right, rectangle.Bottom, rectangle.X, rectangle.Bottom);
                path.AddLine(rectangle.X, rectangle.Bottom, rectangle.X, rectangle.Y);

            }
            else
            {
                path.AddArc(rectangle.X, rectangle.Y, diameter, diameter, 180, 90);
                path.AddLine(rectangle.X + radius, rectangle.Y, rectangle.Right - radius, rectangle.Y);
                path.AddArc(rectangle.Right - diameter, rectangle.Y, diameter, diameter, 270, 90);
                path.AddLine(rectangle.Right, rectangle.Y + radius, rectangle.Right, rectangle.Bottom - radius);
                path.AddArc(rectangle.Right - diameter, rectangle.Bottom - diameter, diameter, diameter, 0, 90);
                path.AddLine(rectangle.Right - radius, rectangle.Bottom, rectangle.X + radius, rectangle.Bottom);
                path.AddArc(rectangle.X, rectangle.Bottom - diameter, diameter, diameter, 90, 90);
                path.AddLine(rectangle.X, rectangle.Bottom - radius, rectangle.X, rectangle.Y + radius);
            }

            path.CloseFigure();
            return path;
        }
        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            /* using (SolidBrush solidBrush = new SolidBrush(Color.Transparent))
             {
                 pevent.Graphics.FillRectangle(solidBrush, 0, 0, Width, Height);
             }
            */
            base.OnPaintBackground(pevent);
        }
        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
            Graphics graphics = pe.Graphics;
            graphics.SmoothingMode = SmoothingMode.HighQuality;
            graphics.PixelOffsetMode = PixelOffsetMode.None;


            using (SolidBrush bgSolidBrush = new SolidBrush(mouseHover ? SystemColors.GradientInactiveCaption : BackgroundColor))
            using (GraphicsPath path = GetRoundRect(new Rectangle(0, 0, Width - 1, Height - 1), Radius))
            {
                graphics.FillPath(bgSolidBrush, path);

                if (Focused || mouseHover)
                {
                    graphics.DrawPath(SystemPens.HotTrack, path);
                }
                else
                {
                    graphics.DrawPath(SystemPens.ControlDark, path);
                }

                Rectangle contentRec = new Rectangle(Padding.Left,
                    Padding.Top,
                    Width - Padding.Left - Padding.Right - 13,
                    Height - Padding.Top - Padding.Bottom);

                using (GraphicsPath arrow = GetArrowRect(new Rectangle(Width - 12, (Height - 4) / 2, 6, 4), 0))
                {
                    graphics.FillPath(Brushes.Black, arrow);
                }
                OnDrawContent(new DrawComboBoxContentEventArgs
                {
                    Graphics = graphics,
                    Bounds = contentRec
                });

            }
        }

        protected virtual void OnComboBoxListOpening()
        {

        }

        /// <summary>
        /// 绘制下拉框主体内容
        /// </summary>
        /// <param name="eventArgs"></param>
        protected virtual void OnDrawContent(DrawComboBoxContentEventArgs eventArgs)
        {

        }
        /// <summary>
        /// 绘制下拉框选项
        /// </summary>
        /// <param name="eventArgs"></param>
        protected virtual void OnDrawItem(DrawItemEventArgs eventArgs)
        {

        }

        protected class DrawComboBoxContentEventArgs : EventArgs
        {
            public Graphics Graphics;
            public Rectangle Bounds;
        }

        protected class DrawItemEventArgs : EventArgs
        {
            public Graphics Graphics;
            public Rectangle Bounds;
            public int Index;
            public bool Hovered;
        }
        protected class ComboBoxSelectedEvent : EventArgs
        {
            public int Index = 0;
            public object Item;
        }

        protected class ComboBoxList : Control
        {
            protected int itemHeight = 30;
            protected int scrollValue = 0;

            protected BaseComboBox comboBox;
            private Rectangle ClientBounds = new Rectangle();
            VScrollBar scrollBar;

            public void NotifyUpdateData()
            {
                SuspendLayout();
                Width = comboBox.Width - 2;
                itemHeight = comboBox.Height;
                //Console.WriteLine(comboBox.Height);
                int height = comboBox.Items.Count * itemHeight;
                int width = Width;
                if (height > comboBox.MaximumHeight)
                {
                    scrollBar.Maximum = height - 1;
                    scrollBar.Minimum = 0;
                    scrollBar.LargeChange = comboBox.MaximumHeight;
                    scrollBar.Dock = DockStyle.Right;
                    // scrollBar.Location = new Point(width - 16, 1);
                    // scrollBar.Anchor = AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom;
                    scrollBar.Size = new Size(13, comboBox.MaximumHeight);

                    width -= 13;
                    scrollBar.Visible = true;
                    height = comboBox.MaximumHeight;

                }
                Height = height;

                ClientBounds.X = 0;
                ClientBounds.Y = 0;
                ClientBounds.Width = width;
                ClientBounds.Height = Height;
                ResumeLayout(false);
            }

            public ComboBoxList(BaseComboBox comboBox)
            {
                SetStyle(ControlStyles.AllPaintingInWmPaint, true);
                SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
                SuspendLayout();
                this.comboBox = comboBox;

                scrollBar = new VScrollBar();
                scrollBar.Scroll += ScrollBar_Scroll;
                scrollBar.Visible = false;
                Controls.Add(scrollBar);
                this.ImeMode = ImeMode.Disable;

                ResumeLayout(false);

            }

            protected virtual void ScrollBar_Scroll(object sender, ScrollEventArgs eventArgs)
            {
                scrollValue = eventArgs.NewValue;
                Invalidate();
            }

            protected override void OnKeyDown(KeyEventArgs e)
            {
                base.OnKeyDown(e);
                //Console.WriteLine("点击了退出");
                switch (e.KeyCode)
                {
                    case Keys.Escape:
                        Close();
                        break;
                }
            }

            protected override void OnMouseWheel(MouseEventArgs e)
            {
                base.OnMouseWheel(e);
                if (scrollBar == null || !scrollBar.Visible)
                    return;
                if (e.Delta > 0)
                {
                    if (scrollValue > scrollBar.Minimum)
                    {
                        scrollBar.Value = Math.Max(scrollBar.Value - itemHeight, scrollBar.Minimum);
                    }
                }
                else
                {
                    if (scrollValue < scrollBar.Maximum - scrollBar.Minimum - scrollBar.LargeChange + 1)
                    {
                        scrollBar.Value = Math.Min(scrollBar.Value + itemHeight,
                            scrollBar.Maximum - scrollBar.Minimum - scrollBar.LargeChange + 1);
                    }
                }
                scrollValue = scrollBar.Value;
                Invalidate();
            }

            protected override void OnPaintBackground(PaintEventArgs pevent)
            {
                base.OnPaintBackground(pevent);
            }

            protected override void OnLostFocus(EventArgs e)
            {
                base.OnLostFocus(e);
               // Console.WriteLine("失去焦点");
            }

            public int HoverIndex = -1;

            ///protected o

            protected override void OnMouseMove(MouseEventArgs e)
            {
                base.OnMouseMove(e);
                if (ClientBounds.Contains(e.X, e.Y))
                {
                    int hit = HitTest(e.X, e.Y);
                    if (HoverIndex != hit)
                    {
                        HoverIndex = hit;
                        Invalidate();
                    }
                }

            }

            public void Close()
            {
                (Parent as NPopup).Close();
            }

            protected override void OnMouseClick(MouseEventArgs e)
            {
                base.OnMouseClick(e);
                if (e.Button == MouseButtons.Left)
                {
                    int hit = HitTest(e.X, e.Y);
                    if (hit >= comboBox.Items.Count)
                        return;
                    comboBox.SelectedIndex = hit;
                    Close();
                }
            }

            private int HitTest(int x, int y)
            {
                return (scrollValue + y) / itemHeight;
            }
            protected override void OnPaint(PaintEventArgs pe)
            {
                Graphics graphics = pe.Graphics;


                int start = scrollValue / itemHeight;
                int startPaint = scrollValue % itemHeight;

                Rectangle rectangle = new Rectangle(0, -startPaint, ClientBounds.Width, itemHeight);

                DrawItemEventArgs drawItemEventArgs = new DrawItemEventArgs()
                {
                    Graphics = graphics,
                    Index = start,
                    Bounds = rectangle,
                };
                int index;
                for (index = start; index < comboBox.Items.Count; index++)
                {
                    drawItemEventArgs.Index = index;
                    drawItemEventArgs.Hovered = index == HoverIndex;
                    comboBox.OnDrawItem(drawItemEventArgs);
                    if (drawItemEventArgs.Bounds.Bottom >= Height)
                        break;
                    drawItemEventArgs.Bounds.Y += itemHeight;
                }
            }


        }



        public class ObjectCollection<T> : IList, ICollection, IEnumerable
        {
            private ArrayList arrayList;

            public ObjectCollection()
            {
                this.arrayList = new ArrayList();
            }

            public object this[int index] { get => arrayList[index]; set => arrayList[index] = value; }

            public bool IsReadOnly => arrayList.IsReadOnly;

            public bool IsFixedSize => arrayList.IsFixedSize;

            public int Count => arrayList.Count;

            public object SyncRoot => arrayList.SyncRoot;

            public bool IsSynchronized => arrayList.IsSynchronized;

            public int Add(object value)
            {
                return arrayList.Add(value);
            }
            public void AddRange(ICollection collection)
            {
                arrayList.AddRange(collection);
            }



            public void Clear()
            {
                arrayList.Clear();
            }

            public bool Contains(object value)
            {
                return arrayList.Contains(value);
            }

            public void CopyTo(Array array, int index)
            {
                arrayList.CopyTo(array, index);
            }

            public IEnumerator GetEnumerator()
            {
                return arrayList.GetEnumerator();
            }

            public int IndexOf(object value)
            {
                return arrayList.IndexOf(value);
            }

            public void Insert(int index, object value)
            {
                arrayList.Insert(index, value);
            }

            public void Remove(object value)
            {
                arrayList.Remove(value);
            }

            public void RemoveAt(int index)
            {
                arrayList.RemoveAt(index);
            }
        }

    }
}
